#include <it.h>

#include "informix_connection.hpp"

namespace topdbs
{
	informix_connection::informix_connection()
		:  m_pConnection(nullptr)
	{
	};

	informix_connection::~informix_connection()
	{
		disConnect();
	};

	int  informix_connection::connect(
		eDBSClient_t dbsClientType,
		const string& dbString,
		const string& user,
		const string& pass,
		void* pOption  ) 
	{
		string host;
		int port;
		m_dbsClient = dbsClientType;
		m_user = user;
		m_pass = pass;
		topdbs_dbstring_parse(dbString,m_dbname,host,port);
		dbslog(DBS_DEBUG,"dbstring=%s,user=%s,pass=***",dbString.c_str(),user.c_str());

		disConnect();
		
		m_pConnection = connection_create();
		return m_pConnection ? ERR_DBS_OK : ERR_DBS_FAIL;
	};

	bool informix_connection::isConnected()  
	{
		return m_pConnection ? m_pConnection->IsOpen() : false;
	};

	int  informix_connection::reConnect()  
	{
		disConnect();
		m_pConnection = connection_create();	
		return m_pConnection ? ERR_DBS_OK : ERR_DBS_FAIL;
	};

	int  informix_connection::disConnect()  
	{
		if(m_pConnection)
		{
			if(isTransaction())
			{
				rollback();
			}
			m_pConnection->Close();
			delete m_pConnection;
			m_pConnection = nullptr;
		}
		return ERR_DBS_OK;
	};	

	int  informix_connection::begin() 
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);
		if(isTransaction())
		{
			return ERR_DBS_OK;
		}
		m_pConnection->SetTransaction(ITConnection::ITTransactionState::BEGIN);
		if( m_pConnection->Error() )
		{
			m_sqlcode = m_pConnection->SqlState().Data();
			m_sqlerr  = m_pConnection->ErrorText().Data();
			dbslog(DBS_ERROR,"begin() TransactionException sqlstat[%s] erortext[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());
			return ERR_DBS_EXCEPTION;
		}
		lockwait();
		dbslog(DBS_DEBUG,"begin Transaction successed.");
		return ERR_DBS_OK;
	};

	int  informix_connection::commit() 
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);
		if(!isTransaction())
		{
			return ERR_DBS_OK;
		}
		m_pConnection->SetTransaction(ITConnection::ITTransactionState::COMMIT);
		if( m_pConnection->Error() )
		{
			m_sqlcode = m_pConnection->SqlState().Data();
			m_sqlerr = m_pConnection->ErrorText().Data();
			dbslog(DBS_ERROR,"commit() TransactionException sqlstat[%s] erortext[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());
			return ERR_DBS_EXCEPTION;
		}	
		dbslog(DBS_DEBUG,"commit Transaction successed.");
		return ERR_DBS_OK;
	};

	int  informix_connection::rollback() 
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);
		if(!isTransaction())
		{
			return ERR_DBS_OK;
		}
		m_pConnection->SetTransaction(ITConnection::ITTransactionState::ABORT);
		if( m_pConnection->Error() )
		{
			m_sqlcode = m_pConnection->SqlState().Data();
			m_sqlerr = m_pConnection->ErrorText().Data();
			dbslog(DBS_ERROR,"rollback() TransactionException sqlstat[%s] erortext[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());
			return ERR_DBS_EXCEPTION;
		}
		dbslog(DBS_DEBUG,"rollback Transaction successed.");
		return ERR_DBS_OK;
	};

	bool informix_connection::isTransaction() 
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,false);
		int isTransactionState = m_pConnection->GetTransactionState();
		dbslog(DBS_TRACE,"TransactionState = [%d]",isTransactionState);
		return  isTransactionState == ITConnection::ITTransactionState::BEGIN ? true : false;
	};

	int  informix_connection::autoCommit(bool on)  
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);
		m_pConnection->SetTransaction(ITConnection::ITTransactionState::AUTO);
		if( m_pConnection->Error() )
		{
			m_sqlcode = m_pConnection->SqlState().Data();
			m_sqlerr = m_pConnection->ErrorText().Data();
			dbslog(DBS_ERROR,"autoCommit() TransactionException sqlstat[%s] erortext[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());
			return ERR_DBS_EXCEPTION;
		}		
		dbslog(DBS_DEBUG,"autoCommit Transaction successed.");
		return ERR_DBS_OK;
	};

	void* informix_connection::getConnection()
	{
		return m_pConnection;
	}

	void* informix_connection::from_this()
	{
		return this;
	}

	ITConnection* informix_connection::connection_create( )
	{
		ITConnection* pConnection = nullptr;
		ITDBInfo dbInfo;
		if(!m_user.empty()) dbInfo.SetUser(ITString(m_user.c_str()));
		if(!m_pass.empty()) dbInfo.SetPassword(ITString(m_pass.c_str()));
		if(!m_dbname.empty()) dbInfo.SetDatabase(ITString(m_dbname.c_str()));    

		try
		{
			pConnection = new ITConnection(dbInfo);	
			if ( !pConnection->Open() )
			{
				if( pConnection->Error() )
				{
					m_sqlcode = pConnection->SqlState().Data();
					m_sqlerr  = pConnection->ErrorText().Data();
					dbslog(DBS_ERROR,"connection_create ConnectionFailedException sqlstat[%s] erortext[%s]", m_sqlcode.c_str(),m_sqlerr.c_str());
				}
				delete (pConnection);
				pConnection = nullptr;
			}          
		}
		catch(const std::exception& e)
		{
			dbslog(DBS_ERROR,"connect() new ITConnection Exception %s !!",e.what());
		} 
		return 	pConnection;
	}

	bool informix_connection::eveironment_check()
	{
		char* psEnvInformixDir=getenv("INFORMIXDIR");
		if(!psEnvInformixDir)
		{
			dbslog(DBS_ERROR,"eveironment_check() Failed: The environment variable INFORMIXSERVER must be set!!");
			return false;
		}

		char* psEnvSqlhosts = getenv("INFORMIXSQLHOSTS");
		if(!psEnvSqlhosts)
		{
			dbslog(DBS_ERROR,"eveironment_check() Failed: The environment variable INFORMIXSQLHOSTS must be set!!");
			return false;
		}

		char* psEnvClientLocale	= getenv("CLIENT_LOCALE");
		if(!psEnvSqlhosts)
		{
			dbslog(DBS_WARN,"The environment variable CLIENT_LOCALE not be set.");
			if(!m_charset.empty())
			{
#ifdef WIN32
				_putenv_s("CLIENT_LOCALE", m_charset.c_str());
#else
				setenv("CLIENT_LOCALE",m_charset.c_str(),1);
#endif				
				dbslog(DBS_WARN,"set the environment variable CLIENT_LOCALE=[%s].",m_charset.c_str());
			}
		}	

		char* psEnvDbLocale	= getenv("DB_LOCALE");
		if(!psEnvDbLocale)
		{
			dbslog(DBS_WARN,"The environment variable DB_LOCALE not be set.");
		}

		ITDBNameList dbnl;
		if(dbnl.Create())
		{
			ITString dbName;
			while(ITString::Null != (dbName = dbnl.NextDBName()) )
			{
				dbslog(DBS_INFO,"The infromix ITDBName=%s",dbName.Data());
			}
		}
		return true;
	}

	int informix_connection::lockwait()
	{	
		char sCommandText[256] = {0};
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);

		if(m_locktime < 1 )
		{
			char* psEnvLockTime = getenv("DB_LOCK_WAIT_TIME");
			if(psEnvLockTime)
			{
				m_locktime = atoi(psEnvLockTime); 
			}		
		}
		if(m_locktime == 0 || m_locktime > 900 ) 
		{
			m_locktime = TOPDBS_DEFAULT_LOCK_WAIT_TIME;
		}	 
		sprintf(sCommandText,"SET LOCK MODE TO WAIT %d",m_locktime);
		return execute(sCommandText);
	}

	char* informix_connection::getClientLocale(char* locale)
	{
		strcpy(locale,ITLocale::Current()->LocaleName());
		return locale;
	}

	char* informix_connection::getDbLocale(const char* db_name,char* locale)
	{
		char sCommandText[1024]={0};
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,locale);

		sprintf(sCommandText,"SELECT dbs_collate FROM sysmaster:sysdbslocale WHERE dbs_dbsname = '%s'",db_name);
		if( execute(sCommandText) == ERR_DBS_OK )
		{
			auto iter = m_row.find("dbs_collate");
			if(iter != m_row.end())
			{
				string value = iter->second;
				dbslog(DBS_DEBUG,"dbs_collate=%s",value.c_str());
				strcpy(locale,value.c_str());
			}
		}	
		return locale;
	}

	int informix_connection::execute(const string& sCommandText )
	{
		CHK_SESSION_HANDLE_ERROR_RETURN(m_pConnection,ERR_DBS_UNKOWN);
		if(IsSelectSQL(sCommandText))
		{
			ITQuery query(*m_pConnection);		
			ITRow* it_row = query.ExecOneRow(sCommandText.c_str());
			if(!it_row)
			{
				if( query.Error() )
				{
					ITString xErrcode(query.SqlState()),xErrText(query.ErrorText()),xIsamText(query.IsamErrorText());
					m_sqlcode = xErrcode.Data();
					m_sqlerr  = xErrText.Data(); 
					dbslog(DBS_ERROR,"Exception sqlstat[%s] erortext[%s] IsamText[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());					
				}
				return ERR_DBS_FAIL;
			}
			for (std::size_t col = 0; col < it_row->NumColumns(); ++col)
			{
				std::string colna,colval;
				ITValue* it_val  = it_row->Column(col);
				colval = it_val->Printable();
				it_val->Release();
				m_row.insert(std::pair<std::string, std::string>(topdbs_string::to_lower(colna), colval));
			}	
			it_row->Release();  
		}
		else
		{
			ITQuery query(*m_pConnection);	
			if( !query.ExecForStatus(sCommandText.c_str()))
			{
				if( query.Error() ) 
				{

					ITString xErrcode(query.SqlState()),xErrText(query.ErrorText()),xIsamText(query.IsamErrorText());
					m_sqlcode = xErrcode.Data();
					m_sqlerr = !xIsamText.IsNull()?xIsamText.Data():"";
					dbslog(DBS_ERROR,"commandText=[%s]",sCommandText.c_str());
					dbslog(DBS_ERROR,"Exception sqlstat[%s] erortext[%s] IsamText[%s]",m_sqlcode.c_str(),m_sqlerr.c_str());	
				}
				return ERR_DBS_FAIL;
			}    
		}
		return ERR_DBS_OK;
	}
}